package com.itheima.service.impl;

import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.datasource.DruidDatasource;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//@Component(value = "userService")//value属性指定bean的名称，同时value可省略
//@Component("userService")//@Component是bean的通用注解，不代表其它标识意义。弊端：通过该注解无法确定当前类是属于三层架构的具体那一层
//@Component//如果不指定bean的名称，那么默认为类名且首字母小写：userServiceImpl
//@Controller//作用等同于@Component,标签语义：大表当前bean是web层的bean对象
//@Service("userService")//作用等同于@Component,标签语义：大表当前bean是服务层的bean对象
@Repository("userService")//作用等同于@Component,标签语义：大表当前bean是dao层的bean对象
//@Scope(value = "singleton")
@Scope("singleton")//@Scope指定bean的作用域，默认单例，可省略不写
//@Scope("prototype")//指定bean为多例模式

//加载外部的properties资源，作用等价于：
//    <context:property-placeholder
//        location="classpath:jdbc.properties"
//        file-encoding="UTF-8"/>
@PropertySource(value = "classpath:jdbc.properties",encoding ="UTF-8" )//文件路径书写格式与xml一致
public class UserServiceImpl implements UserService {
    //通过注解给userName属性赋值，注意事项：如果直接赋值，类型必须是基本类型或者String
    //实际开发很少这样用
//    @Value("zhangsan")
    @Value("${jdbc.username}")//加载外部的properties资源，并赋值
    private String userName;

    /**
     * 说明：
     *  @Autowired注入其它的bean对象，为当前属性赋值
     *      默认根据类型装配（从IOC容器中找UserDao这种类型的bean，为当前属性赋值）
     *      如果容器中出现相同类型的bean，且属性名称（userDao）与bean的名称不一致，那么
     *      会报错：
     *      nested exception is org.springframework.beans.factory.
     *      NoUniqueBeanDefinitionException: No qualifying bean of type 'com.itheima.dao.UserDao' available:
     *      expected single matching bean but found 2: userDao1,userDao2
     *      属性：
     *          required：是否必须
     *              true:默认值，就是必须注入，否则抛异常(开发中一般设置为true，也就是使用默认值)
     *              false:没有对应的bean，导致注入失败，则抛异常，但是要注意：空指针异常
     *  如何解决相同类型的bean注入问题？
     *      方式1：将注入的属性名称与需要注入的bean的名称一致即可（将userDao改为userDao2）
     *          了解即可
     *      方式2:借助注解@Qualifier("userDao2")指定具体注入哪个bean对象（筛选指定的bean对象注入）
     */
//    @Autowired(required = false)
    @Autowired
    @Qualifier("userDao2")
    private UserDao userDao;

    public UserServiceImpl() {
        System.out.println("constructor run....");
    }

    @Override
    public void save() {
//        System.out.println("UserServiceImpl save run...");
        userDao.save();
    }

    /**
     * 定义初始化方法
     */
    @PostConstruct
    public void initM(){
        System.out.println("initM run....");
    }
    /**
     * 定义销毁时触发的方法
     */
    @PreDestroy
    public void destroyM(){
        System.out.println("destroyM run....");
    }


    /**
     *
//     * @param userDao
     * @return
     * @Bean注解作用的方法返回的对象自动被ioc容器关系
     *      注意点：
     *          1.当前方法所属类必须被ioc容器管理；
     *          2.如果没有给bean取名字，默认就是方法名称；
     *          3.如果方法有入参，则会默认根据入参的类型取ioc容器中获取对应的bean
     *          然后作为入参，传入该方法！
     *          如果方法注入的类型的bean有多个，则推荐结合@Qualifier一块适合用
     */
    @Bean("datasource")
    public DruidDatasource myDruidDatasource(@Qualifier("userDao1") UserDao userDao,@Value("${jdbc.username}") String userName){
//    public DruidDatasource myDruidDatasource(){
        System.out.println(userDao);
        DruidDatasource datasource = new DruidDatasource();
        datasource.setJdbcUserName("root");
        datasource.setJdbcUrl("jdbc:mysql:///mysql");
        return datasource;
    }

}
